package com.deuwise.config.oauth2;

import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.deuwise.common.utils.EnumErrorCode;
import com.deuwise.common.utils.SpringContextHolder;
import com.deuwise.system.service.SysUserService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExpiredCredentialsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author  chenwenchao
 * @date 2018-10-04
 * <pre>
 * </pre>
 * <small> 2018年4月22日 | Aron</small>
 */
public class JWTAuthenticationFilter extends BasicHttpAuthenticationFilter {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    /**
     * 判断是否为登录请求
     */
    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        HttpServletRequest req = (HttpServletRequest) request;
        String token= req.getParameter("token");
        //添加兼容文件下载token认证方式
        String authorization = req.getHeader("Authorization");
        // jwtTokenUtil.validateToken(authToken
        return authorization != null||token!=null;
    }

    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) {
        //已经在isAccessAllowed登录过了，不执行父类的登录操作（token不同）
        return false;
    }

    /**
     * 这里返回true，Controller中可以通过 subject.isAuthenticated() 来判断用户是否登入
     * 如果有些资源只有登入用户才能访问，我们只需要在方法上面加上 注解 @RequiresAuthentication
     * 缺点：不能够对GET,POST等请求进行分别过滤鉴权
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (isLoginAttempt(request, response)) {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            String authorization = httpServletRequest.getHeader("Authorization");
            String tokenVal= request.getParameter("token");
            if(org.apache.commons.lang3.StringUtils.isEmpty(authorization)){
                authorization=tokenVal;
            }
            try {
                SpringContextHolder.getBean(SysUserService.class).verifyToken(authorization, false);

                JWTAuthenticationTokenToken token = new JWTAuthenticationTokenToken(authorization);
                getSubject(request, response).login(token);
            }catch (Exception ex){
                //登录认证失效
                if(ex instanceof IncorrectCredentialsException) {
                    onLoginFail(response,EnumErrorCode.apiAuthorizationFailed.getCode(), EnumErrorCode.apiAuthorizationFailed.getMsg());
                }else if(ex instanceof ExpiredCredentialsException) {
                    onLoginFail(response,EnumErrorCode.apiAuthorizationExpired.getCode(), EnumErrorCode.apiAuthorizationExpired.getMsg());

                }else if(ex instanceof TokenExpiredException){
                    onLoginFail(response,EnumErrorCode.apiAuthorizationExpired.getCode(), EnumErrorCode.apiAuthorizationExpired.getMsg());
                }else if(ex instanceof AuthenticationException) {
                     onLoginFail(response,EnumErrorCode.apiAuthorizationLoggedout.getCode(), EnumErrorCode.apiAuthorizationLoggedout.getMsg());
                }

                else{
                    onLoginFail(response,EnumErrorCode.apiAuthorizationExpired.getCode(), EnumErrorCode.apiAuthorizationInvalid.getMsg());
                }

               return  false;
              //  response401(request, response);
            }
            return true;
        }
        return true;
    }
    /**
     * 鉴定失败，返回错误信息
     * @param token
     * @param e
     * @param request
     * @param response
     * @return
     */
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        try {
            ((HttpServletResponse) response).setStatus(HttpStatus.UNAUTHORIZED.value());
            JSONObject jsonObject=new JSONObject();
            jsonObject.put("code",44004);
            jsonObject.put("msg","token认证失败");
            response.getWriter().print(jsonObject);
        } catch (IOException e1) {
            log.error(e1.getMessage(), e1);
        }
        return false;
    }
    /**
     * 当访问拒绝时是否已经处理了；
     * 如果返回true表示需要继续处理；
     * 如果返回false表示该拦截器实例已经处理完成了，将直接返回即可。
     *
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        if (isLoginRequest(request, response)) {
           /* if (isLoginSubmission(request, response)) {*/
                return executeLogin(request, response);
          /*  } else {
                return true;
            }*/
        } else {
            onLoginFail(response,44004,"token认证失败");
            return false;
        }
    }

    /**
     * token 认证失败
     *
     * @param response
     */
    private void onLoginFail(ServletResponse response,Integer code,String msg) {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        ((HttpServletResponse) response).setStatus(HttpStatus.UNAUTHORIZED.value());
        try {
            JSONObject jsonObject=new JSONObject();
            jsonObject.put("code",code);
            jsonObject.put("msg",msg);
            response.setContentType("text/html;charset=UTF-8");
            response.getWriter().print(jsonObject);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }
    /**
     * 跨域处理
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers",
                httpServletRequest.getHeader("Access-Control-Request-Headers"));
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }

    /**
     * 将非法请求跳转到 /401
     */
    private void response401(ServletRequest req, ServletResponse resp) {
        try {
            HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
            httpServletResponse.sendRedirect("/401");
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }

}
